using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ObviousCode.Stately;
using System.IO;
using ObviousCode.Stately.Compiler.Parser;
using MoreLinq;
using ObviousCode.Stately.Compiler.Validation.Expectations;
using Lexer = StatelyCompiler.Lexer;

namespace ObviousCode.StatelyCompiler.Validation
{
    public partial class ValidatorExpectationBuilder
    {
        public void Start(string mapFileName, string codeFileName, Action<ValidationExpectationsContext> callback)
        {
            ValidationExpectationsContext context = new ValidationExpectationsContext();

            context.MapFileName = mapFileName;
            context.CodeFileName = codeFileName;

            context.Callback = callback;

            _stately.Start(Step.LocateMapFile, context);
        }

        private ITransitionRequest HandleLocateMapFile(ValidationExpectationsContext context)
        {
            if (File.Exists(context.MapFileName))
            {
                return _stately.T(Step.LocateCodeFile, context);
            }

            _stately.Derail(Step.NotifyMapFileMissing, context);
            return HaltRequest.Instance;
        }

        private ITransitionRequest HandleLocateCodeFile(ValidationExpectationsContext context)
        {
            if (File.Exists(context.CodeFileName))
            {
                return _stately.T(Step.LexMapFile, context);
            }

            _stately.Derail(Step.NotifyCodeFileMissing, context);
            return HaltRequest.Instance;
        }

        private ITransitionRequest HandleLexMapFile(ValidationExpectationsContext context)
        {
            Lexer lexer = new Lexer();

            using (FileStream stream = File.OpenRead(context.MapFileName))
            {
                context.LexerTokens = lexer.Lex(stream);

                return _stately.T(Step.ParseMapFile, context);
            }
        }

        private ITransitionRequest HandleParseMapFile(ValidationExpectationsContext context)
        {
            Parser parser = new Parser();

            parser.Parse(context.LexerTokens, (d) =>
                {
                    context.TransformData = d;
                });

            return _stately.T(Step.BuildExpectationsList, context);
        }

        private ITransitionRequest HandleBuildExpectationsList(ValidationExpectationsContext context)
        {
            context.DefinedTransitions = new List<DefinedStateTransition>();

            context
                .TransformData
                .SimpleTransitions
                 .ForEach(t =>
                     {
                         DefinedStateTransition stateTransition = new DefinedStateTransition(t.SourceState);

                         context
                             .TransformData
                             .Transitions
                             .Where(inner => inner.SourceState.Data == t.SourceState.Data)
                             .ForEach(expected => stateTransition.ExpectedTransitions.Add(expected)
                             );

                         context.DefinedTransitions.Add(stateTransition);
                     });

            return _stately.T(Step.NotifyExpectationsBuilt, context);
        }

        private ITransitionRequest HandleNotifyExpectationsBuilt(ValidationExpectationsContext context)
        {
            if (context.Callback != null)
            {
                context.Callback(context);
            }

            return CompleteRequest.Instance;

        }

        public event EventHandler MapFileMissing;
        private ITransitionRequest HandleNotifyMapFileMissing(ValidationExpectationsContext context)
        {
            if (MapFileMissing != null)
            {
                MapFileMissing(this, EventArgs.Empty);
            }
            else
            {
                throw new InvalidOperationException("Map file {0} is missing".F(context.MapFileName));
            }

            return HaltRequest.Instance;
        }

        public event EventHandler CodeFileMissing;
        private ITransitionRequest HandleNotifyCodeFileMissing(ValidationExpectationsContext context)
        {
            if (CodeFileMissing != null)
            {
                CodeFileMissing(this, EventArgs.Empty);
            }
            else
            {
                throw new InvalidOperationException("Code file {0} is missing".F(context.CodeFileName));
            }

            return HaltRequest.Instance;
        }
    }
}

